{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Deep Learning with PyTorch Step-by-Step: A Beginner's Guide"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Chapter 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>.container { width:80% !important; }</style>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.core.display import display, HTML\n",
    "display(HTML(\"<style>.container { width:80% !important; }</style>\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    import google.colab\n",
    "    import requests\n",
    "    url = 'https://raw.githubusercontent.com/dvgodoy/PyTorchStepByStep/master/config.py'\n",
    "    r = requests.get(url, allow_redirects=True)\n",
    "    open('config.py', 'wb').write(r.content)    \n",
    "except ModuleNotFoundError:\n",
    "    pass\n",
    "\n",
    "from config import *\n",
    "config_chapter5()\n",
    "# This is needed to render the plots in this chapter\n",
    "from plots.chapter5 import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "import numpy as np\n",
    "from PIL import Image\n",
    "\n",
    "import torch\n",
    "import torch.optim as optim\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "from torch.utils.data import DataLoader, Dataset\n",
    "from torchvision.transforms import Compose, Normalize\n",
    "\n",
    "from data_generation.image_classification import generate_dataset\n",
    "from helpers import index_splitter, make_balanced_sampler\n",
    "from stepbystep.v1 import StepByStep"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Convolutions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Filter / Kernel"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/conv1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 1, 6, 6)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "single = np.array(\n",
    "    [[[[5, 0, 8, 7, 8, 1],\n",
    "       [1, 9, 5, 0, 7, 7],\n",
    "       [6, 0, 2, 4, 6, 6],\n",
    "       [9, 7, 6, 6, 8, 4],\n",
    "       [8, 3, 8, 5, 1, 3],\n",
    "       [7, 2, 7, 0, 1, 0]]]]\n",
    ")\n",
    "single.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 1, 3, 3)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "identity = np.array(\n",
    "    [[[[0, 0, 0],\n",
    "       [0, 1, 0],\n",
    "       [0, 0, 0]]]]\n",
    ")\n",
    "identity.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Convolving"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/conv2.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "9"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "region = single[:, :, 0:3, 0:3]\n",
    "filtered_region = region * identity\n",
    "total = filtered_region.sum()\n",
    "total"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/conv3.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Moving Around"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/stride1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_region = single[:, :, 0:3, (0+1):(3+1)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/conv5.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "new_filtered_region = new_region * identity\n",
    "new_total = new_filtered_region.sum()\n",
    "new_total"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/conv6.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/conv7.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "last_horizontal_region = single[:, :, 0:3, (0+4):(3+4)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "operands could not be broadcast together with shapes (1,1,3,2) (1,1,3,3) ",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-10-fa0fcce9e228>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mlast_horizontal_region\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0midentity\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m: operands could not be broadcast together with shapes (1,1,3,2) (1,1,3,3) "
     ]
    }
   ],
   "source": [
    "last_horizontal_region * identity"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/conv8.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\Large\n",
    "(h_i, w_i) * (h_f, w_f) = (h_i - (h_f - 1), w_i - (w_f - 1))\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\Large\n",
    "(h_i, w_i) * f = (h_i - f + 1, w_i - f + 1)\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Convolving in PyTorch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "image = torch.as_tensor(single).float()\n",
    "kernel_identity = torch.as_tensor(identity).float()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[9., 5., 0., 7.],\n",
       "          [0., 2., 4., 6.],\n",
       "          [7., 6., 6., 8.],\n",
       "          [3., 8., 5., 1.]]]])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "convolved = F.conv2d(image, kernel_identity, stride=1)\n",
    "convolved"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[ 2.2519,  2.6194,  3.8814,  2.6101],\n",
       "          [ 4.3527,  4.1864,  2.0456,  1.8073],\n",
       "          [ 1.2035, -0.6668,  1.1848,  2.8766],\n",
       "          [ 4.0927,  3.0245,  5.2795,  3.4872]]]],\n",
       "       grad_fn=<ThnnConv2DBackward>)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1)\n",
    "conv(image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Parameter containing:\n",
       "tensor([[[[ 0.2716, -0.2576,  0.0734],\n",
       "          [-0.2504, -0.1636, -0.0597],\n",
       "          [-0.2353, -0.1113, -0.1722]]],\n",
       "\n",
       "\n",
       "        [[[ 0.3302, -0.2975, -0.2111],\n",
       "          [ 0.0423, -0.2394,  0.0581],\n",
       "          [-0.0420, -0.1108,  0.2937]]]], requires_grad=True)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conv_multiple = nn.Conv2d(in_channels=1, out_channels=2, kernel_size=3, stride=1)\n",
    "conv_multiple.weight"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "with torch.no_grad():\n",
    "    conv.weight[0] = kernel_identity\n",
    "    conv.bias[0] = 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[9., 5., 0., 7.],\n",
       "          [0., 2., 4., 6.],\n",
       "          [7., 6., 6., 8.],\n",
       "          [3., 8., 5., 1.]]]], grad_fn=<ThnnConv2DBackward>)"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conv(image)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Striding"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/strider2.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/strider3.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\Large\n",
    "(h_i, w_i) * f = \\left(\\frac{h_i - f + 1}{s}, \\frac{w_i - f + 1}{s}\\right)\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[9., 0.],\n",
       "          [7., 6.]]]])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "convolved_stride2 = F.conv2d(image, kernel_identity, stride=2)\n",
    "convolved_stride2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Padding"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/padding1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[0., 0., 0., 0., 0., 0., 0., 0.],\n",
       "          [0., 5., 0., 8., 7., 8., 1., 0.],\n",
       "          [0., 1., 9., 5., 0., 7., 7., 0.],\n",
       "          [0., 6., 0., 2., 4., 6., 6., 0.],\n",
       "          [0., 9., 7., 6., 6., 8., 4., 0.],\n",
       "          [0., 8., 3., 8., 5., 1., 3., 0.],\n",
       "          [0., 7., 2., 7., 0., 1., 0., 0.],\n",
       "          [0., 0., 0., 0., 0., 0., 0., 0.]]]])"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "constant_padder = nn.ConstantPad2d(padding=1, value=0)\n",
    "constant_padder(image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "padded = F.pad(image, pad=(1, 1, 1, 1), mode='constant', value=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](images/paddings.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[5., 5., 0., 8., 7., 8., 1., 1.],\n",
       "          [5., 5., 0., 8., 7., 8., 1., 1.],\n",
       "          [1., 1., 9., 5., 0., 7., 7., 7.],\n",
       "          [6., 6., 0., 2., 4., 6., 6., 6.],\n",
       "          [9., 9., 7., 6., 6., 8., 4., 4.],\n",
       "          [8., 8., 3., 8., 5., 1., 3., 3.],\n",
       "          [7., 7., 2., 7., 0., 1., 0., 0.],\n",
       "          [7., 7., 2., 7., 0., 1., 0., 0.]]]])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "replication_padder = nn.ReplicationPad2d(padding=1)\n",
    "replication_padder(image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[9., 1., 9., 5., 0., 7., 7., 7.],\n",
       "          [0., 5., 0., 8., 7., 8., 1., 8.],\n",
       "          [9., 1., 9., 5., 0., 7., 7., 7.],\n",
       "          [0., 6., 0., 2., 4., 6., 6., 6.],\n",
       "          [7., 9., 7., 6., 6., 8., 4., 8.],\n",
       "          [3., 8., 3., 8., 5., 1., 3., 1.],\n",
       "          [2., 7., 2., 7., 0., 1., 0., 1.],\n",
       "          [3., 8., 3., 8., 5., 1., 3., 1.]]]])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "reflection_padder = nn.ReflectionPad2d(padding=1)\n",
    "reflection_padder(image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[0., 7., 2., 7., 0., 1., 0., 7.],\n",
       "          [1., 5., 0., 8., 7., 8., 1., 5.],\n",
       "          [7., 1., 9., 5., 0., 7., 7., 1.],\n",
       "          [6., 6., 0., 2., 4., 6., 6., 6.],\n",
       "          [4., 9., 7., 6., 6., 8., 4., 9.],\n",
       "          [3., 8., 3., 8., 5., 1., 3., 8.],\n",
       "          [0., 7., 2., 7., 0., 1., 0., 7.],\n",
       "          [1., 5., 0., 8., 7., 8., 1., 5.]]]])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "F.pad(image, pad=(1, 1, 1, 1), mode='circular')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\Large\n",
    "(h_i, w_i) * f = \\left(\\frac{(h_i + 2p) - f + 1}{s}, \\frac{(w_i + 2p) - f + 1}{s}\\right)\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A REAL Filter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([1, 1, 3, 3])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "edge = np.array(\n",
    "    [[[[0, 1, 0],\n",
    "       [1, -4, 1],\n",
    "       [0, 1, 0]]]]\n",
    ")\n",
    "kernel_edge = torch.as_tensor(edge).float()\n",
    "kernel_edge.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/padding2.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/padding3.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "padded = F.pad(image, (1, 1, 1, 1), mode='constant', value=0)\n",
    "conv_padded = F.conv2d(padded, kernel_edge, stride=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pooling"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](./images/pooling1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[22., 23., 11.],\n",
       "          [24.,  7.,  1.],\n",
       "          [13., 13., 13.]]]])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pooled = F.max_pool2d(conv_padded, kernel_size=2)\n",
    "pooled"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[24.]]]])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "maxpool4 = nn.MaxPool2d(kernel_size=4)\n",
    "pooled4 = maxpool4(conv_padded)\n",
    "pooled4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[24., 24., 23., 23.],\n",
       "          [24., 24., 23., 23.],\n",
       "          [24., 24., 13., 13.],\n",
       "          [13., 13., 13., 13.]]]])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "F.max_pool2d(conv_padded, kernel_size=3, stride=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Flattening"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[22., 23., 11., 24.,  7.,  1., 13., 13., 13.]])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "flattened = nn.Flatten()(pooled)\n",
    "flattened"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[22., 23., 11., 24.,  7.,  1., 13., 13., 13.]])"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pooled.view(1, -1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Typical Architecture"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## LeNet5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](images/architecture_lenet.png)\n",
    "\n",
    "*Source: Generated using Alexander Lenail's [NN-SVG](http://alexlenail.me/NN-SVG/) and adapted by the author. For more details, see LeCun, Y., et al (1998).  [Gradient-based learning applied to document recognition](http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf). Proceedings of the IEEE,86(11), 2278–2324*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "lenet = nn.Sequential()\n",
    "\n",
    "# Featurizer\n",
    "# Block 1: 1@28x28 -> 6@28x28 -> 6@14x14\n",
    "lenet.add_module('C1', nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2))\n",
    "lenet.add_module('func1', nn.ReLU())\n",
    "lenet.add_module('S2', nn.MaxPool2d(kernel_size=2))\n",
    "# Block 2: 6@14x14 -> 16@10x10 -> 16@5x5\n",
    "lenet.add_module('C3', nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5))\n",
    "lenet.add_module('func2', nn.ReLU())\n",
    "lenet.add_module('S4', nn.MaxPool2d(kernel_size=2))\n",
    "# Block 3: 16@5x5 -> 120@1x1\n",
    "lenet.add_module('C5', nn.Conv2d(in_channels=16, out_channels=120, kernel_size=5))\n",
    "lenet.add_module('func2', nn.ReLU())\n",
    "# Flattening\n",
    "lenet.add_module('flatten', nn.Flatten())\n",
    "\n",
    "# Classification\n",
    "# Hidden Layer\n",
    "lenet.add_module('F6', nn.Linear(in_features=120, out_features=84))\n",
    "lenet.add_module('func3', nn.ReLU())\n",
    "# Output Layer\n",
    "lenet.add_module('OUTPUT', nn.Linear(in_features=84, out_features=10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A Multiclass Classification Problem"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data Generation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "images, labels = generate_dataset(img_size=10, n_images=1000, binary=False, seed=17)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABB4AAAE0CAYAAACLqfDEAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nOzde1RU9f7/8RcCXkINL+UoMCCWiHe8pRxLu2hapkhqXpK0xEotb5GZ53xLs0jN9FSWy+/p+M1L4u2YZnY8VmoEapmZeNS8oSBomoqICnKZ3x8u5+fEfWY2DPh8rMVaysyw33te89l7z3v27I9bWlqaRQAAAAAAAAaoUt4FAAAAAACAyovGAwAAAAAAMAyNBwAAAAAAYBgaDwAAAAAAwDA0HgAAAAAAgGFoPAAAAAAAAMNU6sbDyZMn5e3trZycnDJ9LOzj7e2t48ePl/ljUTrkVDGQU8XAfqriIKuKg+1fxUBOFUerVq20bdu2Mn8sSs+Vx5VDjYeHHnpIx44d04kTJ/TAAw/Y3Hbx4kUNGzZMjRo1UsuWLbV69Wq7lxMbG6vmzZs7UqrTZWVlaezYsfLz81PTpk310UcflXdJRSoqq0WLFql79+66++679eKLLzq0HFc8uLJYLHrjjTfUuHFjNW7cWP/zP/8ji8VS3mUVqLCcsrKyNG7cOLVs2VK+vr66//77tWXLFruX44o5ff/99+rTp4/MZrNatWpV3uUUqajxNHr0aAUFBcnPz0/t27fXkiVL7F6OK+b0wQcfqEuXLvL19VXr1q31wQcflHdJRSoqq5uOHTumBg0aaPTo0XYvh/2U44rK6vHHH1eDBg3k4+MjHx8fdejQwe7luGJW69atU8+ePdWwYUM9/vjj5V1OkYobU2vXrlWnTp3UqFEjtW3bVvHx8XYth+2fY4rK6eY4uvlTt25dRUVF2bUcV8ypIh1PSEVndfLkSQ0cOFD+/v5q2rSpoqKiHHquXa1h8/nnn6tbt27y8/NT8+bN9T//8z8u9Vr6s6Ky+u233/TEE0/IbDYrJCREX375pd3LccVx5Yz3U3Y3HrKzs5WcnKzAwEDt3btXbdq0sbn9lVdeUdWqVXX48GH97//+ryZPnqyDBw/auziX8+677+r48eNKSEjQl19+qQ8++EDffPNNeZdVoOKyMplMeuWVV/T000+XU4XG+r//+z999dVX+uGHHxQXF6fNmzdr8eLF5V1WPkXllJOTIx8fH3311VdKSkrStGnTNHLkSJ08ebIcK3YuLy8vPf3005oxY0Z5l1Kk4sbTxIkTtW/fPiUnJ2vFihWaOXOm9u7dW07VOp/FYtEnn3yiEydOaO3atVq0aJHWrl1b3mUVqLisbnrllVfUrl27Mq7OeJVpPyVJc+bMUUpKilJSUrR79+5yqNI4derU0YsvvqgJEyaUdylFKi6nrVu36o033tCCBQt06tQpbdq0SQEBAeVTrAEqyvavuJxujqOUlBQdPnxYNWrUUFhYWDlV63wV5XhCKtn7qfr16+u3335TbGys4uLi9I9//KOcqnW+a9euKTo6WseOHdM333yj7du368MPPyzvsgpU3HH60KFD9eijjyoxMVHz58/X888/r6NHj5Zjxc7ljPdTdjceDhw4oKCgILm5uemXX36xefKvXLmiDRs2aNq0aapZs6a6dOmiXr16aeXKlfYurlCbN2/W/fffLz8/P7Vo0ULR0dH57rNs2TI1a9ZMQUFBNi/mvLw8zZs3T23btlXjxo01YsQIXbx4sUTLjYmJUVRUlLy9vRUUFKSIiAh9/vnnTlsvZyoqK0nq27ev+vTpo7p16xpax88//6wePXrIbDYrKChIUVFRun79us19/vOf/6hNmzYKDAzU3/72N+Xl5VlvW7p0qTp16iR/f3+Fh4crKSmpRMtdsWKFxo0bJx8fHzVq1Ehjx451yayKysnLy0tTp06Vv7+/qlSpol69eslsNhvyhra8cmrfvr0GDx7s8gepxY2n4OBgVatWTZLk5uYmNzc3JSYmOr2O8spp/Pjxatu2rTw8PHTvvffqscce086dO526bs5SXFbSjU9n77zzzkLPhnAG9lPFK0lWZaG8surevbv69++vhg0bOm1djFBcTtHR0Xr11VfVsWNHValSRY0aNVKjRo2cXgfbv6KVZjytX79e9evXV2hoqNPr4HiieMVldfLkSfXv31/Vq1dXgwYN9PDDD+vQoUNOryMxMVFPPPGEGjdurMDAQEVGRiotLc3mPnv27NF9990nf39/jRkzRpmZmdbb/v3vf6tr164ym83q2bOn9u/fX6LlPvfccwoNDVXVqlXVqFEjDRw40CXHlFR0VocPH9aZM2c0duxYubu7q1u3brrvvvsUExPj9Doq8vupUjceli1bJrPZrF69eumnn36S2WzWRx99pDfffFNms1knTpzQ0aNH5e7urnvuucf6uFatWhlyxsMdd9yhhQsX6uTJk1q5cqX++c9/auPGjTb3iY2N1c8//6x//etfmj9/vvV7RgsXLtRXX32lr776SocOHZK3t7deeeWVApczb948PfXUU5KktLQ0nT59Wi1btrRZPyM2BI4oSVZlyd3dXe+8846OHz+u//znP9q+fXu+ru3GjRu1bds2bd++XZs2bdKyZcusv3///fe1dOlSHTt2TF26dNGoUaMKXM7q1attdqCHDh1y6azsyens2bM6duyYgoODnV5PeeXk6kqT0+TJk9WwYUN17NhRDRo0UI8ePZxejyvkZLFYtGPHDkNeh44oaVbp6el65513NHPmTEPrYT9VuNKMq+nTpyswMFCPPvqoYmNjDamnPLKqCEqSU25urn755RedP39eISEhat68uaKionTt2jWn18P2r2D2HE+sWLFCgwcPlpubm9PrcYWcXFVJs3rhhRe0du1aXb16Vampqfrmm2/08MMPO70ei8WiSZMm6dChQ/rxxx916tQpvfvuuzb3Wb16tdauXau9e/fq2LFjeu+99yRJe/fu1bhx4zR//nwlJiZqxIgRGjJkiLKysvItZ8eOHTKbzYXWER8f71JjSipZVgV95cBisRjy3rciv58qdePh6aefVlJSktq2bastW7YoLi5OwcHBSk5OVlJSkgICAnTlyhXVrl3b5nG1a9dWRkZGaRdXrPvvv18tWrRQlSpV1LJlSz355JOKi4uzuc+UKVPk5eWlFi1aaNiwYVqzZo2kG6eM/O1vf5OPj4+qVaum1157TevXry/w+zQTJ060nrFxcz1uXcfatWvr8uXLTl8/R5Qkq7LUtm1bdezYUR4eHvL399eIESPyZTVhwgTVqVNHfn5+evHFF22ymjhxooKCguTh4aHJkycrISGhwC7dwIEDbb5TmpGRkS+rjIwMl7nOQ2lzys7OVmRkpIYMGaKmTZs6vZ7yysnVlSanuXPn6tSpU/r666/1xBNPWM+AcCZXyCk6Olp5eXkaNmyY09fPESXN6u2339bw4cPl6+traD3spwpX0qymT5+uvXv36uDBg3rmmWc0ZMgQQ84kKo+sKoKS5HT27FllZ2dr/fr1+vrrrxUbG6t9+/ZZ35w4E9u/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aDwAAAAAAADD0HgAAAAAAACGofEAAAAAAAAMc1s1HqKjozV69OgyfyxKb/ny5erVq1eZPxalQ04VB1lVDOynKg6yqhjY9lUcZFUxxMbGqnnz5mX+WJSeq42pUjUeHnroIR07dkwnTpzQAw88YHPbokWL1L17d91999168cUX8z12+/bt6tixoxo2bKg+ffooKSnJ7qJffPFFzZw50+7HG2H8+PHq0KGD6tSpo+XLl5d3OXZndf36dUVERKhVq1by9vZWbGysQ3W44sHVyZMn1adPHzVs2FAdO3bUtm3byq0We3P66aefFBYWpoCAADVp0kTPPPOMzpw5Y3cdrpbTuXPn9Nxzz6lZs2Yym8169NFHtXv37nKtyd6sDh06pO7du8vf31/+/v7q16+fDh06ZHcdrpaVJM2cOVOhoaGqV6+eoqOjy7UWR/ZTN7377rvy9vZ2aNvgavupo0ePasiQIWrSpIkCAgIUHh6uI0eOlGtN9mZ18uRJeXt7y8fHx/oze/Zsu+sgq6I5MqauXr2qyZMnKzAwUGazWb1797a7Dlfb9lWm/dSqVatsxlPDhg3l7e2tvXv32lWHq2UlVY5jP0lat26dOnXqJF9fX913333auHGj3XW4WrMmKytL48aNU8uWLeXr66v7779fW7ZsKdeaHMlqyZIlCgkJkY+Pj5588kmdPn3a7joqy5gqceMhOztbycnJCgwM1N69e9WmTRub200mk1555RU9/fTT+R57/vx5DR8+XNOmTVNiYqJCQkL07LPPlnTRFULLli01d+7cfM9LeXAkK0nq3LmzFi1apAYNGpRFuWVu1KhRat26tY4fP66//vWvioiI0B9//FHmdTiSU1pamkaMGKF9+/YpISFBNWvW1NixY8uqdMNduXJFISEh2rZtmxITEzVkyBANGjRIGRkZ5VKPI1mZTCZ99tlnOnHihI4fP67evXtXuu1fYGCgpk+frp49e5ZrHY5u+yQpMTFRGzZskMlkMrrcMnXp0iX17t1bu3fv1pEjR9SuXTsNHTq03OpxRlYnT55USkqKUlJS9OqrrxpdcplxpawczWnChAm6ePGifvzxRyUmJpZ7Y9KZKtN+atCgQdaxlJKSovfee08BAQEucUzrLJXh2C81NVWjR4/W22+/reTkZM2YMUORkZE6d+5cWZVvqJycHPn4+Oirr75SUlKSpk2b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1aoaM2aMunTpInd3d0PrnDdvntq2bWvt2n755Zc2t1ssFkVFRclsNqtjx47avn279bZLly5p3LhxCgoKUnBwsGbOnKnc3Nxil3n06FH9+uuvmjp1qmrUqKF+/fqpRYsW2rBhg9PXrziO5NSjRw+FhYWpdu3auuOOOxQZGaldu3YZUuftnpPkWFbe3t7y9/eXm5ubLBaL3N3dlZiYaEidt3tWjuR0U1RUlN588015enoaVif7KedkVRbKI6v27dsrIiJCderUkaenp8aOHasjR47owoULTl23knAkpyNHjujrr7/W/PnzVb9+fbm7u6tt27aG1Fke2z5JGjp0qHr06KFatWo5dX3s4cwxtWLFCg0ePFhubm5Or5P9lP05paam6s4771SPHj3k5uamRx99VHfccYchxxTLli2znlnRpk0bLV68ON995s6dq8DAQLVq1UqrVq2y/j4rK0t//etf1bJlS917772aOHGirl27VuwyXWnbLzmW1b///W+FhYUpODhYVatWVVRUlOLj4w3JqiKNqWIbD8uWLZPZbFavXr30008/yWw266OPPtKbb74ps9msEydO5HvM2bNndezYMQUHB0uSDh48qJYtW1pv9/LyUuPGjXXw4MFiV6y02rVrp9jYWCUmJmrAgAEaMWKEMjMzrbdv2rRJYWFhSkxM1MCBAzVs2DBlZ2crLy9PgwcPVsuWLXXw4EFt2LBBn3zyib799tsClxMaGqrVq1c7vX5HOCOrstS4cWN9/fXXSkpK0pQpU/T888/bfF1g9+7dCggI0LFjxzR16lQNHz5cFy9elHTj1FgPDw/t2bNH33//vb777jstWbKkwOU89dRTmjdvnqQbr8WAgACbg4SbmZcVI3KKj49Xs2bNDKn3ds1Jcm5WZrNZDRo00KuvvqpJkyYZUu/tmpWzcvriiy/k6elp+KeX7KecM6ZatWql5s2ba8yYMTp//rwh9bpCVnFxcWrQoIHq1q1ryDoWxBk57d69W35+foqOjlZgYKBCQ0O1fv16Q+otj22fq3D2MUVSUpLi4+M1ePBgQ+plP2V/TiEhIWratKk2bdqk3Nxcbdy4UdWqVVOLFi2cXu9dd92llStXKjk5WQsWLNDrr79u0wD4/fffdf78eR08eFCffPKJJkyYYP1K2BtvvKGjR48qNjZWe/bsUWpqaqFfh5s8ebImT55c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\n",
      "text/plain": [
       "<Figure size 1080x324 with 30 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plot_images(images, labels, n_plot=30)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data Preparation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TransformedTensorDataset(Dataset):\n",
    "    def __init__(self, x, y, transform=None):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "        self.transform = transform\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        x = self.x[index]\n",
    "        \n",
    "        if self.transform:\n",
    "            x = self.transform(x)\n",
    "        \n",
    "        return x, self.y[index]\n",
    "        \n",
    "    def __len__(self):\n",
    "        return len(self.x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Builds tensors from numpy arrays BEFORE split\n",
    "# Modifies the scale of pixel values from [0, 255] to [0, 1]\n",
    "x_tensor = torch.as_tensor(images / 255).float()\n",
    "y_tensor = torch.as_tensor(labels).long()\n",
    "\n",
    "# Uses index_splitter to generate indices for training and\n",
    "# validation sets\n",
    "train_idx, val_idx = index_splitter(len(x_tensor), [80, 20])\n",
    "# Uses indices to perform the split\n",
    "x_train_tensor = x_tensor[train_idx]\n",
    "y_train_tensor = y_tensor[train_idx]\n",
    "x_val_tensor = x_tensor[val_idx]\n",
    "y_val_tensor = y_tensor[val_idx]\n",
    "\n",
    "# We're not doing any data augmentation now\n",
    "train_composer = Compose([Normalize(mean=(.5,), std=(.5,))])\n",
    "val_composer = Compose([Normalize(mean=(.5,), std=(.5,))])\n",
    "\n",
    "# Uses custom dataset to apply composed transforms to each set\n",
    "train_dataset = TransformedTensorDataset(x_train_tensor, y_train_tensor, transform=train_composer)\n",
    "val_dataset = TransformedTensorDataset(x_val_tensor, y_val_tensor, transform=val_composer)\n",
    "\n",
    "# Builds a weighted random sampler to handle imbalanced classes\n",
    "sampler = make_balanced_sampler(y_train_tensor)\n",
    "\n",
    "# Uses sampler in the training set to get a balanced data loader\n",
    "train_loader = DataLoader(dataset=train_dataset, batch_size=16, sampler=sampler)\n",
    "val_loader = DataLoader(dataset=val_dataset, batch_size=16)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Loss"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Softmax"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\Large\n",
    "\\begin{array}\n",
    "& z & = \\text{logit}(p) & = \\text{log odds ratio }(p) & = \\text{log}\\left(\\frac{p}{1-p}\\right)\n",
    "\\\\\n",
    "e^z & = e^{\\text{logit}(p)} & = \\text{odds ratio }(p) & = \\left(\\frac{p}{1-p}\\right)\n",
    "\\end{array}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\Large\n",
    "\\text{softmax}(z_i) = \\frac{e^{z_i}}{\\sum_{c=0}^{N_c-1}{e^{z_c}}}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\Large\n",
    "\\text{softmax}(z) = \\left[\\frac{e^{z_0}}{e^{z_0}+e^{z_1}+e^{z_2}},\\frac{e^{z_1}}{e^{z_0}+e^{z_1}+e^{z_2}},\\frac{e^{z_2}}{e^{z_0}+e^{z_1}+e^{z_2}}\\right]\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "logits = torch.tensor([ 1.3863,  0.0000, -0.6931])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([4.0000, 1.0000, 0.5000])"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "odds_ratios = torch.exp(logits)\n",
    "odds_ratios"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0.7273, 0.1818, 0.0909])"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "softmaxed = odds_ratios / odds_ratios.sum()\n",
    "softmaxed"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([0.7273, 0.1818, 0.0909]), tensor([0.7273, 0.1818, 0.0909]))"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nn.Softmax(dim=-1)(logits), F.softmax(logits, dim=-1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Negative Log Likelihood Loss"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "\\Large\n",
    "\\texttt{BCE}(y)={-\\frac{1}{(N_{\\text{pos}}+N_{\\text{neg}})}\\Bigg[{\\sum_{i=1}^{N_{\\text{pos}}}{\\text{log}(\\text{P}(y_i=1))} + \\sum_{i=1}^{N_{\\text{neg}}}{\\text{log}(1 - \\text{P}(y_i=1))}}\\Bigg]}\n",
    "\\\\\n",
    "\\Large\n",
    "\\texttt{NLLLoss}(y)={-\\frac{1}{(N_0+N_1+N_2)}\\Bigg[{\\sum_{i=1}^{N_0}{\\text{log}(\\text{P}(y_i=0))} + \\sum_{i=1}^{N_1}{\\text{log}(\\text{P}(y_i=1))} + \\sum_{i=1}^{N_2}{\\text{log}(\\text{P}(y_i=2))}}\\Bigg]}\n",
    "\\\\\n",
    "\\Large \\texttt{NLLLoss}(y)={-\\frac{1}{(N_0+\\cdots+N_{C-1})}\\sum_{c=0}^{C-1}{\\sum_{i=1}^{N_c}{\\text{log}(\\text{P}(y_i=c))} }}\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([-0.3185, -1.7048, -2.3979])"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "log_probs = F.log_softmax(logits, dim=-1)\n",
    "log_probs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(2.3979)"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "label = torch.tensor([2])\n",
    "F.nll_loss(log_probs.view(-1, 3), label)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[-1.5229, -0.3146, -2.9600],\n",
       "        [-1.7934, -1.0044, -0.7607],\n",
       "        [-1.2513, -1.0136, -1.0471],\n",
       "        [-2.6799, -0.2219, -2.0367],\n",
       "        [-1.0728, -1.9098, -0.6737]])"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.manual_seed(11)\n",
    "dummy_logits = torch.randn((5, 3))\n",
    "dummy_labels = torch.tensor([0, 0, 1, 2, 1])\n",
    "dummy_log_probs = F.log_softmax(dummy_logits, dim=-1)\n",
    "dummy_log_probs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(1.6553)"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "relevant_log_probs = torch.tensor([-1.5229, -1.7934, -1.0136, -2.0367, -1.9098])\n",
    "-relevant_log_probs.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(1.6553)"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "loss_fn = nn.NLLLoss()\n",
    "loss_fn(dummy_log_probs, dummy_labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(1.7188)"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "loss_fn = nn.NLLLoss(weight=torch.tensor([1., 1., 2.]))\n",
    "loss_fn(dummy_log_probs, dummy_labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(1.5599)"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "loss_fn = nn.NLLLoss(ignore_index=2)\n",
    "loss_fn(dummy_log_probs, dummy_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Cross Entropy Loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(1.6553)"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.manual_seed(11)\n",
    "dummy_logits = torch.randn((5, 3))\n",
    "dummy_labels = torch.tensor([0, 0, 1, 2, 1])\n",
    "\n",
    "loss_fn = nn.CrossEntropyLoss()\n",
    "loss_fn(dummy_logits, dummy_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model Configuration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "torch.manual_seed(13)\n",
    "model_cnn1 = nn.Sequential()\n",
    "\n",
    "# Featurizer\n",
    "# Block 1: 1@10x10 -> n_channels@8x8 -> n_channels@4x4\n",
    "n_channels = 1\n",
    "model_cnn1.add_module('conv1', nn.Conv2d(in_channels=1, out_channels=n_channels, kernel_size=3))\n",
    "model_cnn1.add_module('relu1', nn.ReLU())\n",
    "model_cnn1.add_module('maxp1', nn.MaxPool2d(kernel_size=2))\n",
    "# Flattening: n_channels * 4 * 4\n",
    "model_cnn1.add_module('flatten', nn.Flatten())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Classification\n",
    "# Hidden Layer\n",
    "model_cnn1.add_module('fc1', nn.Linear(in_features=n_channels*4*4, out_features=10))\n",
    "model_cnn1.add_module('relu2', nn.ReLU())\n",
    "# Output Layer\n",
    "model_cnn1.add_module('fc2', nn.Linear(in_features=10, out_features=3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](images/classification_softmax.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "lr = 0.1\n",
    "multi_loss_fn = nn.CrossEntropyLoss(reduction='mean')\n",
    "optimizer_cnn1 = optim.SGD(model_cnn1.parameters(), lr=lr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [],
   "source": [
    "sbs_cnn1 = StepByStep(model_cnn1, multi_loss_fn, optimizer_cnn1)\n",
    "sbs_cnn1.set_loaders(train_loader, val_loader)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "sbs_cnn1.train(20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = sbs_cnn1.plot_losses()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualizing Filters and More!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [],
   "source": [
    "@staticmethod\n",
    "def _visualize_tensors(axs, x, y=None, yhat=None, \n",
    "                       layer_name='', title=None):\n",
    "    # The number of images is the number of subplots in a row\n",
    "    n_images = len(axs)\n",
    "    # Gets max and min values for scaling the grayscale\n",
    "    minv, maxv = np.min(x[:n_images]), np.max(x[:n_images])\n",
    "    # For each image\n",
    "    for j, image in enumerate(x[:n_images]):\n",
    "        ax = axs[j]\n",
    "        # Sets title, labels, and removes ticks\n",
    "        if title is not None:\n",
    "            ax.set_title(f'{title} #{j}', fontsize=12)\n",
    "        shp = np.atleast_2d(image).shape\n",
    "        ax.set_ylabel(\n",
    "            f'{layer_name}\\n{shp[0]}x{shp[1]}',\n",
    "            rotation=0, labelpad=40\n",
    "        )\n",
    "        xlabel1 = '' if y is None else f'\\nLabel: {y[j]}'\n",
    "        xlabel2 = '' if yhat is None else f'\\nPredicted: {yhat[j]}'\n",
    "        xlabel = f'{xlabel1}{xlabel2}'\n",
    "        if len(xlabel):\n",
    "            ax.set_xlabel(xlabel, fontsize=12)\n",
    "        ax.set_xticks([])\n",
    "        ax.set_yticks([])\n",
    "\n",
    "        # Plots weight as an image\n",
    "        ax.imshow(\n",
    "            np.atleast_2d(image.squeeze()),\n",
    "            cmap='gray', \n",
    "            vmin=minv, \n",
    "            vmax=maxv\n",
    "        )\n",
    "    return\n",
    "\n",
    "setattr(StepByStep, '_visualize_tensors', _visualize_tensors)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Static Method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Cat(object):\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    @staticmethod\n",
    "    def meow():\n",
    "        print('Meow')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Meow\n"
     ]
    }
   ],
   "source": [
    "Cat.meow()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Visualizing Filters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 1, 3, 3)"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "weights_filter = model_cnn1.conv1.weight.data.cpu().numpy()\n",
    "weights_filter.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [],
   "source": [
    "def visualize_filters(self, layer_name, **kwargs):\n",
    "    try:\n",
    "        # Gets the layer object from the model\n",
    "        layer = self.model\n",
    "        for name in layer_name.split('.'):\n",
    "            layer = getattr(layer, name)\n",
    "        # We are only looking at filters for 2D convolutions\n",
    "        if isinstance(layer, nn.Conv2d):\n",
    "            # Takes the weight information\n",
    "            weights = layer.weight.data.cpu().numpy()\n",
    "            # weights -> (channels_out (filter), channels_in, H, W)\n",
    "            n_filters, n_channels, _, _ = weights.shape\n",
    "\n",
    "            # Builds a figure\n",
    "            size = (2 * n_channels + 2, 2 * n_filters)\n",
    "            fig, axes = plt.subplots(n_filters, n_channels, \n",
    "                                     figsize=size)\n",
    "            axes = np.atleast_2d(axes)\n",
    "            axes = axes.reshape(n_filters, n_channels)\n",
    "            # For each channel_out (filter)\n",
    "            for i in range(n_filters):    \n",
    "                StepByStep._visualize_tensors(\n",
    "                    axes[i, :],\n",
    "                    weights[i],\n",
    "                    layer_name=f'Filter #{i}', \n",
    "                    title='Channel'\n",
    "                )\n",
    "                    \n",
    "            for ax in axes.flat:\n",
    "                ax.label_outer()\n",
    "\n",
    "            fig.tight_layout()\n",
    "            return fig\n",
    "    except AttributeError:\n",
    "        return\n",
    "    \n",
    "setattr(StepByStep, 'visualize_filters', visualize_filters)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 288x144 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = sbs_cnn1.visualize_filters('conv1', cmap='gray')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Hooks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "dummy_model = nn.Linear(1, 1)\n",
    "\n",
    "dummy_list = []\n",
    "\n",
    "def dummy_hook(layer, inputs, outputs):\n",
    "    dummy_list.append((layer, inputs, outputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<torch.utils.hooks.RemovableHandle at 0x7f72b5bc02d0>"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dummy_handle = dummy_model.register_forward_hook(dummy_hook)\n",
    "dummy_handle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([-0.8366], grad_fn=<AddBackward0>)"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dummy_x = torch.tensor([0.3])\n",
    "dummy_model.forward(dummy_x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[]"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dummy_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([-0.8366], grad_fn=<AddBackward0>)"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dummy_model(dummy_x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(Linear(in_features=1, out_features=1, bias=True),\n",
       "  (tensor([0.3000]),),\n",
       "  tensor([-0.8366], grad_fn=<AddBackward0>))]"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dummy_list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "dummy_handle.remove()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('', Sequential(\n",
       "    (conv1): Conv2d(1, 1, kernel_size=(3, 3), stride=(1, 1))\n",
       "    (relu1): ReLU()\n",
       "    (maxp1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "    (flatten): Flatten(start_dim=1, end_dim=-1)\n",
       "    (fc1): Linear(in_features=16, out_features=10, bias=True)\n",
       "    (relu2): ReLU()\n",
       "    (fc2): Linear(in_features=10, out_features=3, bias=True)\n",
       "  )),\n",
       " ('conv1', Conv2d(1, 1, kernel_size=(3, 3), stride=(1, 1))),\n",
       " ('relu1', ReLU()),\n",
       " ('maxp1',\n",
       "  MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)),\n",
       " ('flatten', Flatten(start_dim=1, end_dim=-1)),\n",
       " ('fc1', Linear(in_features=16, out_features=10, bias=True)),\n",
       " ('relu2', ReLU()),\n",
       " ('fc2', Linear(in_features=10, out_features=3, bias=True))]"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "modules = list(sbs_cnn1.model.named_modules())\n",
    "modules"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{Conv2d(1, 1, kernel_size=(3, 3), stride=(1, 1)): 'conv1',\n",
       " ReLU(): 'relu1',\n",
       " MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False): 'maxp1',\n",
       " Flatten(start_dim=1, end_dim=-1): 'flatten',\n",
       " Linear(in_features=16, out_features=10, bias=True): 'fc1',\n",
       " ReLU(): 'relu2',\n",
       " Linear(in_features=10, out_features=3, bias=True): 'fc2'}"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "layer_names = {layer: name for name, layer in modules[1:]}\n",
    "layer_names"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [],
   "source": [
    "visualization = {}\n",
    "\n",
    "def hook_fn(layer, inputs, outputs):\n",
    "    name = layer_names[layer]\n",
    "    visualization[name] = outputs.detach().cpu().numpy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [],
   "source": [
    "layers_to_hook = ['conv1', 'relu1', 'maxp1', 'flatten', 'fc1', 'relu2', 'fc2']\n",
    "\n",
    "handles = {}\n",
    "\n",
    "for name, layer in modules:\n",
    "    if name in layers_to_hook:\n",
    "        handles[name] = layer.register_forward_hook(hook_fn)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [],
   "source": [
    "images_batch, labels_batch = next(iter(val_loader))\n",
    "logits = sbs_cnn1.predict(images_batch)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict_keys(['conv1', 'relu1', 'maxp1', 'flatten', 'fc1', 'relu2', 'fc2'])"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "visualization.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "for handle in handles.values():\n",
    "    handle.remove()\n",
    "handles = {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "setattr(StepByStep, 'visualization', {})\n",
    "setattr(StepByStep, 'handles', {})\n",
    "\n",
    "def attach_hooks(self, layers_to_hook, hook_fn=None):\n",
    "    # Clear any previous values\n",
    "    self.visualization = {}\n",
    "    # Creates the dictionary to map layer objects to their names\n",
    "    modules = list(self.model.named_modules())\n",
    "    layer_names = {layer: name for name, layer in modules[1:]}\n",
    "\n",
    "    if hook_fn is None:\n",
    "        # Hook function to be attached to the forward pass\n",
    "        def hook_fn(layer, inputs, outputs):\n",
    "            # Gets the layer name\n",
    "            name = layer_names[layer]\n",
    "            # Detaches outputs\n",
    "            values = outputs.detach().cpu().numpy()\n",
    "            # Since the hook function may be called multiple times\n",
    "            # for example, if we make predictions for multiple mini-batches\n",
    "            # it concatenates the results\n",
    "            if self.visualization[name] is None:\n",
    "                self.visualization[name] = values\n",
    "            else:\n",
    "                self.visualization[name] = np.concatenate([self.visualization[name], values])\n",
    "\n",
    "    for name, layer in modules:\n",
    "        # If the layer is in our list\n",
    "        if name in layers_to_hook:\n",
    "            # Initializes the corresponding key in the dictionary\n",
    "            self.visualization[name] = None\n",
    "            # Register the forward hook and keep the handle in another dict\n",
    "            self.handles[name] = layer.register_forward_hook(hook_fn)\n",
    "\n",
    "def remove_hooks(self):\n",
    "    # Loops through all hooks and removes them\n",
    "    for handle in self.handles.values():\n",
    "        handle.remove()\n",
    "    # Clear the dict, as all hooks have been removed\n",
    "    self.handles = {}\n",
    "    \n",
    "setattr(StepByStep, 'attach_hooks', attach_hooks)\n",
    "setattr(StepByStep, 'remove_hooks', remove_hooks)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [],
   "source": [
    "sbs_cnn1.attach_hooks(layers_to_hook=['conv1', 'relu1', 'maxp1', 'flatten', 'fc1', 'relu2', 'fc2'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [],
   "source": [
    "images_batch, labels_batch = next(iter(val_loader))\n",
    "logits = sbs_cnn1.predict(images_batch)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [],
   "source": [
    "sbs_cnn1.remove_hooks()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 1, 2, 1, 2, 0])"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "predicted = np.argmax(logits, 1)\n",
    "predicted"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Visualizing Feature Maps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x108 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plot_images(images_batch.squeeze(), labels_batch.squeeze(), n_plot=10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [],
   "source": [
    "def visualize_outputs(self, layers, n_images=10, y=None, yhat=None):\n",
    "    layers = filter(lambda l: l in self.visualization.keys(), layers)\n",
    "    layers = list(layers)\n",
    "    shapes = [self.visualization[layer].shape for layer in layers]\n",
    "    n_rows = [shape[1] if len(shape) == 4 else 1 \n",
    "              for shape in shapes]\n",
    "    total_rows = np.sum(n_rows)\n",
    "\n",
    "    fig, axes = plt.subplots(total_rows, n_images, \n",
    "                             figsize=(1.5*n_images, 1.5*total_rows))\n",
    "    axes = np.atleast_2d(axes).reshape(total_rows, n_images)\n",
    "    \n",
    "    # Loops through the layers, one layer per row of subplots\n",
    "    row = 0\n",
    "    for i, layer in enumerate(layers):\n",
    "        start_row = row\n",
    "        # Takes the produced feature maps for that layer\n",
    "        output = self.visualization[layer]\n",
    "            \n",
    "        is_vector = len(output.shape) == 2\n",
    "        \n",
    "        for j in range(n_rows[i]):\n",
    "            StepByStep._visualize_tensors(\n",
    "                axes[row, :],\n",
    "                output if is_vector else output[:, j].squeeze(),\n",
    "                y, \n",
    "                yhat, \n",
    "                layer_name=layers[i] \\\n",
    "                           if is_vector \\\n",
    "                           else f'{layers[i]}\\nfil#{row-start_row}',\n",
    "                title='Image' if (row == 0) else None\n",
    "            )\n",
    "            row += 1\n",
    "            \n",
    "    for ax in axes.flat:\n",
    "        ax.label_outer()\n",
    "\n",
    "    plt.tight_layout()\n",
    "    return fig\n",
    "\n",
    "setattr(StepByStep, 'visualize_outputs', visualize_outputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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jRo1SrVq1bqe01Hlqkse1Ym7Z3L48GFt2LBBr776qgICAuTp6Wn7Y8EduXsef/Tpp5+qT58+hv9b40rcPY9ffvlFderUUZcuXeTh4aH7779ftWvXdtvvdXfP48svv1T37t11++23q1atWnr22We1bds2p+RxXX/hBAYGys/PTwcOHLhimyZNmujw4cNltpdua9KkSaVeu2fPnsrPz9d//vMfrV27VhaLRY888kil+nK1jIwM2/T9Rx99VCNHjtSePXu0bt06paamasKECTp37pwkqXbt2poyZYq2b9+ud999Vx988IFt+tPBgwf1yiuvKDU1VV9//bXy8/PtpuIsXLhQ69ev1+LFi/X111/L399fEyZMuOK4Bg8erA4dOuj//u//9Oqrr6p9+/Y6c+aMunTpoqeeekqS9OOPP8pkMtn2uemmm3Trrbe63TTr33PnPKqj6pTHtm3bdPvttzvw7LhedcijQ4cOCg8P16uvvqo///nPTjhLruPueXz22WeqWbOmunTp4qQz5FrunockPfDAA7rvvvuUkpIis9nshLPkWu6cSUZGhpo0aaK0tDRFRUXpscce09q1a514tpzPnfP4vRMnTmj79u3q3bu3g8+Qa7lzHmFhYWrZsqW++OILFRcXa/369apVq5bd3yXuxp3zsFqtl52AcLW6QKVZr1NSUpLVZDJZv/vuuzLPlZSUWCdPnmwNCQmxbt682bb94sWL1n79+lnvvPNOa25urtVqtVqzsrKsISEh1nfffbdMPw888IA1KSnJbltxcbH1nnvusQ4fPtw6ePBg60MPPXTVcV6tf2d74IEHrJs2bbJarVbr0qVL7cb6ww8/WENCQqzZ2dm2bZGRkda9e/detq/XXnvN+ve//91qtVqt6enp1hEjRtieKygosLZp08b2Wt27d7d+8803tudPnTplveOOO6wWi+WKYz18+LD1f/7nf6xWq9X6zjvvWGfPnm33fEpKijU1NdVuW//+/a1Lly698gkwmOqUx+/de++91m+//faKzxtVdc1j37591rvvvtu6bdu2K7Yxouqax7lz56yLFy+2fvnll1dsY0TVKY/8/HzrQw89ZD127FiZY3MX1S2PjIwMq8VisWZnZ1uHDh1qHTJkSLnOg5FUp0zeeecda0hIiDUtLc1aWFho3bJlizUiIsJ68ODBcp0LI6hOefzejBkzrAkJCVd83qiqWx4fffSRNSIiwtq6dWtreHg43+lVmMc333xjjYyMtO7bt896/vx569ixY60mk8m6cuXKcp2Lirjuu4SMHDlSmzZt0qBBg2y3Nf3111+1bt06zZgxQ08//bRWr16tv/zlL3riiScUFBSkNWvWaNeuXUpJSbG7Q0hF1KhRQ927d9fHH38si8Wi//f//t9l2y1evFi5ubnKy8uTJG3ZskVFRUWSpCeeeKLSr389AgMDbf/v4+MjSbr55ptt27y9vW3VtN27d+v111/Xjz/+KIvFoosXL6p79+6SpNOnT6thw4a2/WrXrq169erZHp88eVLPPfec3aUCNWrU0JkzZ9SgQQO7MS1evFhvvvmmLl68KOnSv0ieO3dON910k2bNmqW1a9cqMDBQN910k/Lz8+32PXfunHx9fa/rnFQld86jOqoOeRw9elRPP/20/va3v112QWJ3Uh3ykC7NBouPj1enTp20Zs0at/38uHMe6enp6tWrl2655RZHnY4q5855+Pr6qm3btrYxjx07Vp07d1Z+fr78/Pwccn6qgjtn4uPjo5o1a+ovf/mLvLy8FBkZqaioKG3cuFEtW7Z01ClyKXfO4/f++c9/uv0MPcm98/jmm2/0+uuva+HChWrTpo0yMzP17LPPavbs2W57uZs759GpUycNGzZMw4YNU15engYNGiRfX1+7cTjKdRcsgoOD9fHHH+utt97SmjVrlJubq+DgYHXu3Fn169eXr6+vPvzwQ02bNk1LlixRQUGBmjdvrilTpqhPnz7X9dqPPvqoFi1aJElXvBxk7ty5dgt7bty4URs3bpR0aTGdqihYVMSLL76ohIQEvffee/L29tbf//53/fbbb5IunfsjR47Y2l64cMHueqSGDRtq4sSJuuuuu675OgkJCUpISNCTTz6p559/Xq1atdJjjz2mf//733btbr/9drvrqQoKCnTs2DHbIqrVndHyuNEZMY8TJ05o8ODBevbZZ6/7Z5y7MWIev1dSUqLz58/r1KlTbluwqAij5bF582b98ssv+uCDDyRdWsT5hRde0FNPPXXFf3SoToyWxx+VXpdvvcIaY9WR0TJx56ntjmC0PEp99913On36tB5++OHrO0A3Y7Q89u3bpw4dOtgKreHh4QoPD9c333zjtgWLijBaHpI0YMAA252Mjhw5onfeeccpl0I7ZJW+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jRpq0aKFEhMT1bNnTyeMHgAAAAAAuJJLChbHjx9X37595evrq/j4eAUGBmr37t167733tHXrVi1ZsqRC/c2fP1+TJk1S586dNWLECBUVFWnFihUaMWKEcnJyFB8f76QjAQAAAAAAruCSgsXy5cuVn5+v999/X6GhoZKkfv36ydfXVwsWLNChQ4fUsmXLcve3aNEitWnTRu+99548PDwkSf3791fXrl21bNkyChYAAAAAALg5l6xhce7cOUlScHCw3fbSxz4+Ptq8ebNCQ0M1bdo0uzZfffWVTCaTZsyYYduWn5+voKAgW7GitA9/f3/5+Pg46zAAAAAAAICLuKRgERUVJUlKSUnR999/r19++UXr1q3TnDlz1KtXLzVp0kSdOnVSQkKC5syZo507d0qScnJyNGbMGLVp00bPPPOMXX9fffWV5s+fr6ysLP30009KTU3V0aNH9dRTT7nikAAAAAAAgBO55JKQ+++/X0OHDtV7772n2NhY2/a4uDiNHz/e9njUqFHauHGjkpOTtXz5ck2YMEE5OTmaN2+evLz+O9Rx48YpJydHkyZN0qRJkyRJfn5+mjlzprp06eKKQwIAAAAAAE7ksruENG7cWG3btlX37t3VoEEDbd++XYsWLVKtWrU0ZswYSZcu65g6dari4uI0cOBA7d69W8nJyWrVqpVdXz4+PrrtttsUGBiorl27ymKx6JNPPtELL7yg2bNnq0OHDq46LAAAAAAA4AQuKVisXLlS48eP15o1a3TLLbdIkrp27ap69epp+vTp6tWrl9q2bStJCg8P18CBAzV37ly1b99egwYNKtPfsGHD5OHhoTlz5ti2PfLII+rdu7deeeUVrVy50hWHBQAAAAC4wR09elRnz569aptnn33WRaOpXlyyhsWHH36o0NBQW7GiVNeuXSVJ3333nW2bxWLRt99+K0k6efKk8vPz7fbJysrSpk2b9OCDD9pt9/LyUpcuXXTgwAHl5uY64zAAAAAAAICLuKRg8euvv6q4uLjM9qKiIkmye27mzJnau3evkpKSdObMGb322mtl+vrjPqVKt5X2CwAAAAAA3JNLChbNmzfX/v379eOPP9ptX7FihSSpTZs2kqQ9e/Zo9uzZiouL05AhQzRs2DAtX75c69evt+1z2223qUaNGlq9erVKSkps28+fP69169apcePGCggIcMFRAQAA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f/933GfNmjW57LLL8r3vfe+E7atXr87w4cOzcOHChm19+vRJ165dT/u6//jHP7J69epMnTo1PXr0yKFDh3LkyJHmvQkAAACgKG1y0c2rr746X/ziF7No0aL85S9/SZLs378/999/fy6//PJMnz69yTPXrFmTJOnVq1emTJmSkSNHZuTIkZk2bVp27dpV0fUDAAAAbavN7hIyY8aMDBo0KNXV1XnrrbfyzW9+M/v378+8efPSqVPTf5myffv2JP/9WUi3bt3ywx/+MDNmzMj69etz55135q233qrwOwAAAADaSouvYdFYXbt2zbx583LLLbfk9ttvz1//+tdUV1dn2LBhzZr35ptvJvnvNTR+8pOf5Lzz/tu9XHjhhbn33nvz9NNPZ9KkSRVbPwAAANB22uwMiyT54Ac/2FBWjB49OnfccUezZx2/zsX111/fUFYkySc/+cl07tw569evb+lyAQAAgDOkTQuLI0eOZO3atUmSf/3rXzl48GCzZ/Xr1y9J0rt37xO2d+zYMRdccEEOHDjQ/IUCAAAAZ1SbFhYPP/xw/va3v2XWrFnZs2dPvvWtbzV71uWXX54kefXVV0/Yfvjw4ezduzc9e/Zs0VoBAACAM6fNCouNGzfmkUceyS233JKpU6fma1/7Wp566qn87ne/a9a8D3/4w+ndu3dWrFiRw4cPN2xfvnx56urqcs0111Rq6QAAAEAb61BfX1/f0iE1NTU5cOBA/v3vf+fRRx/NtddemzFjxiRJpkyZki5dumTixIk5fPhwli9fnu7du6euri633nprduzYkRUrVqRXr15Jkl27dmX58uVJkrVr12bdunWZNm1aqqqqMmDAgEyYMKHhdZ966qnMmjUrI0eOzA033JDdu3dn8eLFGTFiRGpqapp19xEAAADgzKtIYTFu3Ljs2rXrlI8999xzqampyS9+8YssWbIkV155ZcNj27dvz4QJE/Kxj30s8+fPT5KsW7cut9122ylnXXXVVVmyZMkJ25555pn89Kc/zdatW3P++efnU5/6VO677768613vaunbOsGhQ4eyadOm9O3bNx07dqzo7HNVXV1damtrM2LEiIaLqDaWPCpPHmWRR1nkURZ5lEcmZZFHWeRRFnmU5XR5VKSwOBe8+OKLufXWW8/0Ms5Kjz/++AlFVmPIo/XIoyzyKIs8yiKP8sikLPIoizzKIo+yvF0efjPRSH379k2S7NixI0ePHj3Dqzk7dOrUKYMGDWo4tk0hj8qTR1laI49t27adcv8hQ4Y0b5HvMLc1ZrbW3MbMbMvPx7lwPJs683/n+nyUt1bfIWWRR1nkUZZK5HHPPfc06iYPb/fLgf+1ePHiFj2/PTtdHgqLRjp+ys/Ro0f9ZVFhzTmdSh6tRx5lqWQeAwcOPOX+Lc3sVHNbY2ZrzW3KzLb4fJxLx7OxM99urs9H689t6kzfIWWRR1nkUZaW5NGzZ8/07t37tPs3NrO3m3UuZf52ebTpbU0BAAAAGkNhAQAAABTHT0Iaqa6uLkncKrWCjh/L48e2KeRRefIoS2vksXPnznd8reY61dzWmNlacxszsy0/H+fC8WzqzP+d6/NR3lp9h5RFHmWRR1kqkce+ffua9Fqns2fPnhY9vz07XR5n/xGokNra2iTJoEGDzvBKzj61tbW5+OKLm/ycRB6tQR5lqWQe48ePP+X+Q4cObd7i3mFua8xsrblNmdkWn49z6Xg2dubbzfX5aP25TZ3pO6Qs8iiLPMrSkjx+9KMfNWr/xv4dOmfOnBY9/2zwdnm4rWkjuedu5bkHclnkURZ5lEUeZZFHeWRSFnmURR5lkUdZTpeHwgIAAAAojotuAgAAAMVRWAAAAADFUVgAAAAAxVFYAAAAAMX5f6XVUa/9cJ2SAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 1080x432 with 40 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "featurizer_layers = ['conv1', 'relu1', 'maxp1', 'flatten']\n",
    "\n",
    "with plt.style.context('seaborn-white'):\n",
    "    fig = sbs_cnn1.visualize_outputs(featurizer_layers)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Visualizing Classifier Layers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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nj3Vr18/BQQEqE6dOho9erR27tzpikPjMZU9k3r16qlFixby8vKSZVmqWbOmkpOTXXFoPKayZ1Jkzpw5mj59unx8fJw5HB5XVfKoSip7Jj/99JM2bNig559/XoGBgapZs6btfxAqo8qex6XWrFmjQYMGycvLqzyHwwiVPZPjx4+rbt266tmzp7y8vPSHP/xBderUqdTze2XPZOPGjerbt69atWolHx8fTZ48Wdu3b3dPJpaDDh8+bLVu3drq0aOHNX78eKt169bWW2+95ejml5k/f77Vpk0ba+DAgdYdd9xhtW7d2kpNTS1x3ffff99q3bq19cADD1j/+c9/rOeff9664YYbrOjo6HL37w69evWyvvnmG8uyLOu9996zbrzxRuvdd9+18vPzrVdffdXq2bOn9eyzz1o5OTnW119/bbVv397KzMy0LMuy4uPjrX379lkFBQXWDz/8YHXp0sVat26dZVmWdfDgQat9+/bW9u3brZycHOull16y2rRpY+tr2bJl1tChQ61jx45ZOTk51qxZs6xHH3201HGOGzfO6tixo3XjjTdaYWFhVlhYmHXDDTdYHTt2tO677z7Lsizr+eeft2bPnl1su9tvv9369NNPXX7c3KkqZXKx7t27W/Hx8a4+XG5XVfO4uI/KpipmUrReSEiIFRsb647D5lZVLZOPP/7YevDBBy/bt8qiKuVR9Ltct27drO7du1vR0dHWqVOn3Hn43KIqZbJmzRrrjjvusP7yl79YERER1h133MHvWgbMI5ZlWUeOHLFuuOEGKzk52dWHzO2qUib5+fnWqFGjrPXr11v5+fnWunXrrO7du1tZWVnuPIQuV5UyefHFF61nnnnGts3x48et1q1b28bkSg4XK3Jycqzjx49blvX/k50zxYq0tDQrOzvbsqwLhYvSihU5OTlW586drbFjxxZ7vWib77//vtxjcLVLfwj79OljW7Zv3z6rdevWVlpamu21iIgIa+/evSW2NXfuXOsvf/mLZVmWFRMTU+yH6ty5c1bbtm1tffXt29fasmWLbfmJEyesNm3aWHl5eaWO9aeffrIGDx5sWZZlvfHGG9bixYuLLZ8xY4b1P//zP8VeGz58uPXee++VfgAMVJUyuVhVKVZUlTx++OEHq1OnTtb27dtLXcdUVTWTrKwsa8WKFdbGjRtLXcdUVSmTzMxMq0+fPrZf9qtCsaKy57Fnzx4rLy/PSktLs6ZMmWLde++9Dh0Hk1SlTN544w2rdevW1vz5862cnBwrISHBat++vXXo0CGHjoUJqlIeF1uwYIE1evToUpebrKplsmrVKqt9+/bWjTfeaLVr14653cOZbNmyxYqIiLB++OEHKzs725o1a5YVEhJiffjhhw4diyvh7egVGD4+PmrcuHGZ62zdulXjx4/XhAkTin1uZdOmTZowYYKmTJmihx9+WJLUqFEjh/qNj49Xenq6Ro8eXez1UaNGKTY2Vp988kmJn8c0QcOGDW1f165dW1Lx/fb19VVWVpYkaffu3frrX/+qgwcPKi8vT7m5uerbt68kKTU1VU2aNLFtV6dOHV111VW271NSUvTQQw8V+2hAjRo1dOrUqcsyW7Fihf72t78pNzdXkhQeHq6srCz5+fnpzTff1GeffaaGDRvKz89PmZmZxbbNysqSv7+/U8fE0ypzJlVRVcjj119/1YQJE/TUU08pPDzc6WPiaVUhE0ny8/PTyJEj1aVLF3388ceV+hyqzJnExMRowIABatmypasOh8dV5jz8/f1100032cY8a9YsdevWTZmZmQoICHDJ8fGEypxJ7dq1VatWLU2aNEne3t6KiIhQZGSkNm/erN///veuOkQVqjLncbG1a9dq4sSJTh0LU1TmTLZs2aK//vWv+sc//qG2bdsqKSlJkydP1uLFiyv1x9wqcyZdunTR1KlTNXXqVJ09e1bjxo2Tv79/sXG4iks/+N6lSxeNHj1aS5Ys0a5duyRduAndzJkz1bZtWz344INX3ObevXslyTa5FgkMDFTLli1tyyu7adOmqXfv3vrqq6/07bffasSIEbIsS9KFm8ZdfIfV8+fPF/usUZMmTbR48WLt2LHD9i8xMbHE4tLo0aO1Y8cOderUSe+88442btyoxo0b69tvv9WOHTtsJ06rVq20b98+23bnzp1TcnKyrr/+encdAuOYlkl1Z2IeR48e1fjx4zV58mQNGjTIjXtvJhMzuVhhYaGys7PddodqE5mWydatW7V8+XJ17dpVXbt21bFjx/TnP//ZqXteVSam5XGpos/hF42pOjAtk5CQEDfvsdlMy6PIt99+q9TUVN12221u2nNzmZbJDz/8oPDwcN10002qUaOG2rVrp3bt2lWqJ2s4y7RMpAsXDnz++efaunWrbr31VhUU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\n",
      "text/plain": [
       "<Figure size 1080x324 with 30 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "classifier_layers = ['fc1', 'relu2', 'fc2']\n",
    "\n",
    "with plt.style.context('seaborn-white'):\n",
    "    fig = sbs_cnn1.visualize_outputs(classifier_layers, y=labels_batch, yhat=predicted)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [],
   "source": [
    "def correct(self, x, y, threshold=.5):\n",
    "    self.model.eval()\n",
    "    yhat = self.model(x.to(self.device))\n",
    "    y = y.to(self.device)\n",
    "    self.model.train()\n",
    "    \n",
    "    # We get the size of the batch and the number of classes \n",
    "    # (only 1, if it is binary)\n",
    "    n_samples, n_dims = yhat.shape\n",
    "    if n_dims > 1:        \n",
    "        # In a multiclass classification, the biggest logit\n",
    "        # always wins, so we don't bother getting probabilities\n",
    "        \n",
    "        # This is PyTorch's version of argmax, \n",
    "        # but it returns a tuple: (max value, index of max value)\n",
    "        _, predicted = torch.max(yhat, 1)\n",
    "    else:\n",
    "        n_dims += 1\n",
    "        # In binary classification, we NEED to check if the\n",
    "        # last layer is a sigmoid (and then it produces probs)\n",
    "        if isinstance(self.model, nn.Sequential) and \\\n",
    "           isinstance(self.model[-1], nn.Sigmoid):\n",
    "            predicted = (yhat > threshold).long()\n",
    "        # or something else (logits), which we need to convert\n",
    "        # using a sigmoid\n",
    "        else:\n",
    "            predicted = (F.sigmoid(yhat) > threshold).long()\n",
    "    \n",
    "    # How many samples got classified correctly for each class\n",
    "    result = []\n",
    "    for c in range(n_dims):\n",
    "        n_class = (y == c).sum().item()\n",
    "        n_correct = (predicted[y == c] == c).sum().item()\n",
    "        result.append((n_correct, n_class))\n",
    "    return torch.tensor(result)\n",
    "\n",
    "setattr(StepByStep, 'correct', correct)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[5, 7],\n",
       "        [3, 3],\n",
       "        [6, 6]])"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sbs_cnn1.correct(images_batch, labels_batch)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Loader Apply"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [],
   "source": [
    "@staticmethod\n",
    "def loader_apply(loader, func, reduce='sum'):\n",
    "    results = [func(x, y) for i, (x, y) in enumerate(loader)]\n",
    "    results = torch.stack(results, axis=0)\n",
    "\n",
    "    if reduce == 'sum':\n",
    "        results = results.sum(axis=0)\n",
    "    elif reduce == 'mean':\n",
    "        results = results.float().mean(axis=0)\n",
    "    \n",
    "    return results\n",
    "\n",
    "setattr(StepByStep, 'loader_apply', loader_apply)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[59, 67],\n",
       "        [55, 62],\n",
       "        [71, 71]])"
      ]
     },
     "execution_count": 85,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "StepByStep.loader_apply(sbs_cnn1.val_loader, sbs_cnn1.correct)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Putting It All Together"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Data Preparation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Builds tensors from numpy arrays BEFORE split\n",
    "# Modifies the scale of pixel values from [0, 255] to [0, 1]\n",
    "x_tensor = torch.as_tensor(images / 255).float()\n",
    "y_tensor = torch.as_tensor(labels).long()\n",
    "\n",
    "# Uses index_splitter to generate indices for training and\n",
    "# validation sets\n",
    "train_idx, val_idx = index_splitter(len(x_tensor), [80, 20])\n",
    "# Uses indices to perform the split\n",
    "x_train_tensor = x_tensor[train_idx]\n",
    "y_train_tensor = y_tensor[train_idx]\n",
    "x_val_tensor = x_tensor[val_idx]\n",
    "y_val_tensor = y_tensor[val_idx]\n",
    "\n",
    "# We're not doing any data augmentation now\n",
    "train_composer = Compose([Normalize(mean=(.5,), std=(.5,))])\n",
    "val_composer = Compose([Normalize(mean=(.5,), std=(.5,))])\n",
    "\n",
    "# Uses custom dataset to apply composed transforms to each set\n",
    "train_dataset = TransformedTensorDataset(x_train_tensor, y_train_tensor, transform=train_composer)\n",
    "val_dataset = TransformedTensorDataset(x_val_tensor, y_val_tensor, transform=val_composer)\n",
    "\n",
    "# Builds a weighted random sampler to handle imbalanced classes\n",
    "sampler = make_balanced_sampler(y_train_tensor)\n",
    "\n",
    "# Uses sampler in the training set to get a balanced data loader\n",
    "train_loader = DataLoader(dataset=train_dataset, batch_size=16, sampler=sampler)\n",
    "val_loader = DataLoader(dataset=val_dataset, batch_size=16)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Model Configuration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {},
   "outputs": [],
   "source": [
    "torch.manual_seed(13)\n",
    "model_cnn1 = nn.Sequential()\n",
    "\n",
    "# Featurizer\n",
    "# Block 1: 1@10x10 -> n_channels@8x8 -> n_channels@4x4\n",
    "n_channels = 1\n",
    "model_cnn1.add_module('conv1', nn.Conv2d(in_channels=1, out_channels=n_channels, kernel_size=3))\n",
    "model_cnn1.add_module('relu1', nn.ReLU())\n",
    "model_cnn1.add_module('maxp1', nn.MaxPool2d(kernel_size=2))\n",
    "# Flattening: n_channels * 4 * 4\n",
    "model_cnn1.add_module('flatten', nn.Flatten())\n",
    "\n",
    "# Classification\n",
    "# Hidden Layer\n",
    "model_cnn1.add_module('fc1', nn.Linear(in_features=n_channels*4*4, out_features=10))\n",
    "model_cnn1.add_module('relu2', nn.ReLU())\n",
    "# Output Layer\n",
    "model_cnn1.add_module('fc2', nn.Linear(in_features=10, out_features=3))\n",
    "\n",
    "lr = 0.1\n",
    "multi_loss_fn = nn.CrossEntropyLoss(reduction='mean')\n",
    "optimizer_cnn1 = optim.SGD(model_cnn1.parameters(), lr=lr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Model Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [],
   "source": [
    "sbs_cnn1 = StepByStep(model_cnn1, multi_loss_fn, optimizer_cnn1)\n",
    "sbs_cnn1.set_loaders(train_loader, val_loader)\n",
    "sbs_cnn1.train(20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Visualizing Filters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 288x144 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig_filters = sbs_cnn1.visualize_filters('conv1', cmap='gray')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Capturing Outputs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [],
   "source": [
    "featurizer_layers = ['conv1', 'relu1', 'maxp1', 'flatten']\n",
    "classifier_layers = ['fc1', 'relu2', 'fc2']\n",
    "\n",
    "sbs_cnn1.attach_hooks(layers_to_hook=featurizer_layers + classifier_layers)\n",
    "\n",
    "images_batch, labels_batch = next(iter(val_loader))\n",
    "logits = sbs_cnn1.predict(images_batch)\n",
    "predicted = np.argmax(logits, 1)\n",
    "\n",
    "sbs_cnn1.remove_hooks()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Visualizing Feature Maps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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okaOHKlq1ardyumocJUlj8rE3TM5duyYNm/erFdffVX+/v7y9PS0/rHgjtw9j9/79NNP1atXL8P/rXE97p7HL7/8oho1aqhTp07y8PDQI488ourVq7vt97q75/Hll1+qa9euuueee1StWjX9+c9/1s6dO52Sxy39hRMQECA/Pz8dPnz4um0aNmyoY8eOldpesq1hw4bleu3u3bsrLy9P//nPf7RhwwaZzWY9+eST5erL1VJTU63T95966imNGDFC+/fv18aNG5WSkqKJEyfq4sWLkqTq1atr6tSp2rVrl95991198MEH1ulPR44c0YQJE5SSkqKvv/5aeXl5NlNxlixZok2bNmnZsmX6+uuvVatWLU2cOPG64xo4cKDatWun//u//9Orr76qtm3bKjMzU506ddLzzz8vSfrxxx9lMpms+9xxxx1q3Lix202z/i13zqMyqkx57Ny5U/fcc48Dz47rVYY82rVrp7CwML366qv64x//6ISz5Drunsdnn32mqlWrqlOnTk46Q67l7nlI0qOPPqqHH35YycnJysrKcsJZci13ziQ1NVUNGzbUzJkzFRkZqaefflobNmxw4tlyPnfO47dOnz6tXbt2qWfPng4+Q67lznmEhoaqefPm+uKLL1RUVKRNmzapWrVqNn+XuBt3zsNisVxzAsKN6gLlZrlFiYmJFpPJZPnuu+9KPVdcXGyZMmWKJTg42LJt2zbr9itXrlj69Oljue+++yw5OTkWi8ViSU9PtwQHB1vefffdUv08+uijlsTERJttRUVFlgcffNAybNgwy8CBAy2PP/74Dcd5o/6d7dFHH7Vs3brVYrFYLCtWrLAZ6w8//GAJDg62ZGRkWLdFRERYDhw4cM2+XnvtNcvf//53i8ViscyaNcsyfPhw63P5+fmWVq1aWV+ra9eulm+++cb6/NmzZy333nuvxWw2X3esx44ds/zP//yPxWKxWN555x3LvHnzbJ5PTk62pKSk2Gzr27evZcWKFdc/AQZTmfL4rYceesjy7bffXvd5o6qseRw8eNDywAMPWHbu3HndNkZUWfO4ePGiZdmyZZYvv/zyum2MqDLlkZeXZ3n88cctJ0+eLHVs7qKy5ZGammoxm82WjIwMy5AhQyyDBg2y6zwYSWXK5J133rEEBwdbZs6caSkoKLBs377dEh4ebjly5Ihd58IIKlMevzV79mxLfHz8dZ83qsqWx0cffWQJDw+3tGzZ0hIWFsZ3egXm8c0331giIiIsBw8etFy6dMkyZswYi8lksqxZs8auc1EWt3yXkBEjRmjr1q0aMGCA9bamv/76qzZu3KjZs2frhRde0Lp16/SnP/1Jzz77rAIDA7V+/Xrt3btXycnJNncIKYsqVaqoa9eu+vjjj2U2m/X//t//u2a7ZcuWKScnR7m5uZKk7du3q7CwUJL07LPPlvv1b0VAQID1/318fCRJd955p3Wbt7e3tZq2b98+vf766/rxxx9lNpt15coVde3aVZJ07tw51atXz7pf9erVVbt2bevjM2fOaPDgwTaXClSpUkWZmZmqW7euzZiWLVumN998U1euXJF09V8kL168qDvuuENz587Vhg0bFBAQoDvuuEN5eXk2+168eFG+vr63dE4qkjvnURlVhjxOnDihF154QX/961+vuSCxO6kMeUhXZ4PFxcWpQ4cOWr9+vdt+ftw5j1mzZqlHjx666667HHU6Kpw75+Hr66vWrVtbxzxmzBh17NhReXl58vPzc8j5qQjunImPj4+qVq2qP/3pT/Ly8lJERIQiIyO1ZcsWNW/e3FGnyKXcOY/f+uc//+n2M/Qk987jm2++0euvv64lS5aoVatWSktL05///GfNmzfPbS93c+c8OnTooKFDh2ro0KHKzc3VgAED5OvrazMOR7nlgkVQUJA+/vhjvfXWW1q/fr1ycnIUFBSkjh07qk6dOvL19dWHH36o6dOna/ny5crPz1fTpk01depU9erV65Ze+6mnntLSpUsl6bqXgyxYsMBmYc8tW7Zoy5Ytkq4uplMRBYuyeOWVVxQfH6/33ntP3t7e+vvf/67z589Lunrujx8/bm17+fJlm+uR6tWrp0mTJun++++/6evEx8crPj5ezz33nF566SW1aNFCTz/9tP7973/btLvnnntsrqfKz8/XyZMnrYuoVnZGy+N2Z8Q8Tp8+rYEDB+rPf/7zLf+MczdGzOO3iouLdenSJZ09e9ZtCxZlYbQ8tm3bpl9++UUffPCBpKuLOL/88st6/vnnr/uPDpWJ0fL4vZLr8i3XWWOsMjJaJu48td0RjJZHie+++07nzp3TE088cWsH6GaMlsfBgwfVrl07a6E1LCxMYWFh+uabb9y2YFEWRstDkvr162e9k9Hx48f1zjvvOOVSaIes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AAAAAMNx2SUhx44d08yZM7Vnzx5duHBB9erVU5cuXfTCCy+oZs2aZe5v3bp1WrRokY4fP64qVaqoWbNmSkhIUPfu3Z0wegAAAAAA4EouKVicOnVKvXv3lq+vr+Li4hQQEKB9+/bpvffe044dO7R8+fIy9bdo0SJNnjxZHTt21PDhw1VYWKjVq1dr+PDhys7OVlxcnJOOBAAAAAAAuIJLCharVq1SXl6e3n//fYWEhEiS+vTpI19fXy1evFhHjx5V8+bN7e5v6dKlatWqld577z15eHhIkvr27avOnTtr5cqVFCwAAAAAAHBzLlnD4uLFi5KkoKAgm+0lj318fLRt2zaFhIRo+vTpNm2++uormUwmzZ4927otLy9PgYGB1mJFSR+1atWSj4+Psw4DAAAAAAC4iEsKFpGRkZKk5ORkff/99/rll1+0ceNGzZ8/Xz169FDDhg3VoUMHxcfHa/78+dqzZ48kKTs7W6NHj1arVq304osv2vT31VdfadGiRUpPT9dPP/2klJQUnThxQs8//7wrDgkAAAAAADiRSy4JeeSRRzRkyBC99957iomJsW6PjY3VuHHjrI9HjhypLVu2KCkpSatWrdLEiROVnZ2thQsXysvrv0MdO3assrOzNXnyZE2ePFmS5Ofnpzlz5qhTp06uOCQAAAAAAOBELrtLSIMGDdS6dWt17dpVdevW1a5du7R06VJVq1ZNo0ePlnT1so5p06YpNjZW/fv31759+5SUlKQWLVrY9OXj46O7775bAQEB6ty5s8xmsz755BO9/PLLmjdvntq1a+eqwwIAAAAAAE7gkoLFmjVrNG7cOK1fv1533XWXJKlz586qXbu2ZsyYoR49eqh169aSpLCwMPXv318LFixQ27ZtNWDAgFL9DR06VB4eHpo/f75125NPPqmePXtqwoQJWrNmjSsOCwAAAABwmztx4oTOnz9/wzaDBw920WgqF5esYfHhhx8qJCTEWqwo0blzZ0nSd999Z91mNpv17bffSpLOnDmjvLw8m33S09O1detWPfbYYzbbvby81KlTJx0+fFg5OTnOOAwAAAAAAOAiLilY/PrrryoqKiq1vbCwUJJsnpszZ44OHDigxMREZWZm6rXXXivV1+/3KVGyraRfAAAAAADgnlxSsGjatKkOHTqkH3/80Wb76tWrJUmtWrWSJO3fv1/z5s1TbGys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YD1nnUOHDmXTpk3p1atX2rZte6aX0yrU1NSkuro6Q4cOTceOHSs6Wx4NJ4+yyKMszZlHIpOGkkdZ5FEWeZTHd3pZ5FGWhuahsAAAAACK46KbAAAAQHEUFgAAAEBxFBYAAABAcRQWAAAAQHEUFgAAAEBxFBYAAABAcRQWAAAAQHEUFgAAAEBxFBYAAABAcRQWAAAAQHEUFgAAAEBxFBYAAABAcRQWAAAAQHEUFgAAAEBxFBYAAABAcRQWAAAAQHEUFgAAAEBxFBYAAABAcRQWAAAAQHEUFgAAAEBxFBYAAABAcRQWAAAAQHEUFgAAAEBxFBYAAABAcepdWGzatClf+MIXcuWVV2bIkCF58sknM2TIkOzcubM51wcAAACcg9rVZ6eampp8/etfz7FjxzJz5sx07tw5r776aqNf9MUXX8yaNWty++23593vfvcJjy1ZsiSdO3fOZz/72UbPBwAAAFq3ep1hsXv37uzYsSOTJ0/OpEmTctNNN6Vnz56NftE///nPWbBgQQ4cOHDSY0uWLMmyZcsaPRsAAABo/epVWOzduzdJTjobAgAAAKA5tKmtra19px2qqqpOOuPhoosuyt13353Zs2fnueeeS79+/ZL896ceixcvzoYNG1JdXZ0LLrggY8aMyYwZM+rKjvnz52fBggUnvc6iRYsye/bs7Nq166TXev7555Mkhw8fziOPPJKnn346u3btSteuXTN27NgT5ifJmDFjMnDgwEyfPj0PPvhgNm/enG7duuXOO+/MHXfc0fCjBAAAALSo017DYuLEienXr1/mz5+fiRMnZuTIkenSpcspf86xcuXK7N+/PzfffHN69eqVv//97/nVr36Vl19+OU888USS5LrrrsvWrVvz7LPPZvbs2enWrVuS5JJLLsk3vvGNzJkzJ+eff36mTZuWJOnSpUuSpLa2NnfffXfWrl2bz33uc7nsssuyY8eOPP7449m8eXOeeOKJtG/fvm4tO3fuzLRp0zJhwoTccMMNWblyZR588MEMHjw41157bdOPHAAAANBsTltYXHnllWnTpk3mz5+f4cOH56abbkqSPPnkkyftO2PGjHTq1OmEbcOGDcvMmTOzfv36jBw5Mpdffnne+9735tlnn824cePqzs5IknHjxuW73/1uevbsWfc6x61YsSKrV6/Oz3/+84waNapu+9VXX5277rorzzzzTMaPH1+3ffv27Xnsscfy4Q9/OEly880352Mf+1iWLl2qsAAAAIDC1fu2pvVxvKyora3NwYMHs3fv3owYMSJJsnnz5ibNXrlyZQYMGJDLLrsse/furfvv/e9/fzp37px169adsP+AAQPqyook6dChQ4YNG+Y2rAAAANAK1Ou2pvW1e/fuPPTQQ1m1alXeeOONEx471U9IGmL79u3Ztm1brrnmmlM+vmfPnhP+3Ldv35P26dq1a7Zs2dKkdQAAAADNr2KFxbFjxzJlypTs3bs3X/nKV3LJJZekU6dOOXbsWL785S/nNNf2rNf8wYMH57777jvl48evhXHceedV9OQRAAAAoAVVrLDYsmVLtm7dmrlz52bChAl127dv396gOW3atDnl9v79+2fz5s0ZNWqUMgIAAADOchX7l//xEuF/z6R49NFHT9q3c+fOSU79M5FOnTpl//79J23/1Kc+lddffz1Lliw56bGjR4+e8jkAAABA61SxMywGDRqUAQMG5Dvf+U5eeeWVdO3aNb///e/zyiuvnLTv0KFDkyTf//7385nPfCbt27fPqFGj0qNHjwwdOjRLly7NggULMmDAgHTu3DljxozJjTfemN/+9rd54IEH8qc//Skf/OAH06ZNm/zzn//Mb37zm1RVVeXTn/50pd4OAAAAcAZVrLBo3759fvzjH+eBBx7IwoUL07Zt23zkIx/Jz372s4wePfqEfYcPH5577rknv/zlLzN79uwcO3YsixYtSo8ePfLVr341r732Wh577LEcPHgwF110UcaMGZPzzjsv8+bNy+LFi7Ns2bKsWrUqHTp0SN++fXPjjTfmqquuqtRbAQAAAM6wNrVNvRomAAAAQIW5eiUAAABQHIUFAAAAUByFBQAAAFAchQUAAABQHIUFAAAAUByFBQAAAFAchQUAAABQnHZNHfDGG29k4cKF2bhxYzZu3Jh9+/bl3nvvzV133dWoeRs2bMiyZcuyYcOGbNmyJUeOHMkf/vCH9OrV65T7v/7665k3b15eeOGF7N27N717986oUaPy7W9/uylvCwAAADiDmlxY7Nu3Lw8//HDe85735H3ve1/++Mc/NmneqlWrsnTp0lx66aUZOHBgXn755bfdd/fu3Zk0aVLatGmTiRMnpk+fPnnttdeyYcOGJq0BAAAAOLOaXFj07t07q1evTp8+fbJz586MHTu2SfMmTZqUqVOnpmPHjpk/f/47Fhb3339/2rdvn1//+tfp2rVrk14XAAAAKEeTr2HRoUOH9OnT5x33WbNmTS6//PJ873vfO2H76tWrM2TIkCxYsKBuW8+ePdOxY8fTvu4//vGPrF69OlOmTEnXrl1z6NChHDlypHFvAgAAAChKi1x085prrskXv/jFLFy4MH/5y1+SJPv37899992XK664ItOmTWvwzDVr1iRJunfvnsmTJ2fYsGEZNmxYpk6dml27dlV0/QAAAEDLarG7hMyYMSP9+/dPVVVV3nrrrXzzm9/M/v3789BDD6Vdu4b/MmX79u1J/vuzkE6dOuWHP/xhZsyYkfXr1+fOO+/MW2+9VeF3AAAAALSUJl/Dor46duyYhx56KLfccktuv/32/PWvf01VVVUGDx7cqHlvvvlmkv9eQ+MnP/lJzjvvv93LhRdemHvuuSdPP/10Jk6cWLH1AwAAAC2nxc6wSJIPfOADdWXFiBEjcscddzR61vHrXFx//fV1ZUWSfPzjH0/79u2zfv36pi4XAAAAOENatLA4cuRI1q5dmyT517/+lYMHDzZ6Vu/evZMkPXr0OGF727Ztc8EFF+TAgQONXygAAABwRrVoYfHwww/nb3/7W2bNmpU9e/bkW9/6VqNnXXHFFUmSV1999YTthw8fzt69e9OtW7cmrRUAAAA4c1qssNi4cWMeeeSR3HLLLZkyZUq+9rWv5amnnsrvfve7Rs370Ic+lB49emTFihU5fPhw3fbly5enpqYmo0ePrtTSAQAAgBbWpra2trapQ5YsWZIDBw7k3//+dx599NFce+21GTlyZJJk8uTJ6dChQyZMmJDDhw9n+fLl6dKlS2pqanLrrbdmx44dWbFiRbp3754k2bVrV5YvX54kWbt2bdatW5epU6emc+fO6du3b8aPH1/3uk899VRmzZqVYcOG5YYbbsju3buzaNGiDB06NEuWLGnU3UcAAACAM68ihcWYMWOya9euUz723HPPZcmSJfnFL36RxYsX56qrrqp7bPv27Rk/fnw++tGPZt68eUmSdevW5bbbbjvlrKuvvjqLFy8+YdszzzyTn/70p9m6dWvOP//8fOITn8i9996bd73rXU19Wyc4dOhQNm3alF69eqVt27YVnX2uqqmpSXV1dYYOHVp3EdX6kkflyaMs8iiLPMoij/LIpCzyKIs8yiKPspwuj4oUFueCF198MbfeeuuZXsZZ6fHHHz+hyKoPeTQfeZRFHmWRR1nkUR6ZlEUeZZFHWeRRlrfLw28m6qlXr15Jkh07duTo0aNneDVnh3bt2qV///51x7Yh5FF58ihLc+Sxbdu2U+4/cODAxi3yHeY2x8zmmlufmS35+TgXjmdDZ/7vXJ+P8tbqO6Qs8iiLPMpSiTymT59er5s8vN0vB/7XokWLmvT81ux0eSgs6un4KT9Hjx71l0WFNeZ0Knk0H3mUpZJ59OvX75T7NzWzU81tjpnNNbchM1vi83EuHc/6zny7uT4fzT+3oTN9h5RFHmWRR1makke3bt3So0eP0+5f38zebta5lPnb5dGitzUFAAAAqA+FBQAAAFAcPwmpp5qamiRxq9QKOn4sjx/bhpBH5cmjLM2Rx86dO9/xtRrrVHObY2Zzza3PzJb8fJwLx7OhM/93rs9HeWv1HVIWeZRFHmWpRB779u1r0Gudzp49e5r0/NbsdHmc/UegQqqrq5Mk/fv3P8MrOftUV1fn4osvbvBzEnk0B3mUpZJ5jB079pT7Dxo0qHGLe4e5zTGzueY2ZGZLfD7OpeNZ35lvN9fno/nnNnSm75CyyKMs8ihLU/L40Y9+VK/96/t36Jw5c5r0/LPB2+Xhtqb15J67leceyGWRR1nkURZ5lEUe5ZFJWeRRFnmURR5lOV0eCgsAAACgOC66CQAAABRHYQEAAAAUR2EBAAAAFEdhAQAAABTn/wAZj3KpspMt7gAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 1080x432 with 40 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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z6Nnz57q27evAgICVKdOHY0aNUo7d+50xaGpNJ6eSb169dSiRQt5eXnJsizVrFlTycnJrjg0lcbTMykye/ZsTZs2TT4+Ps4cjkpXVfKoSjw9k59++knr16/X888/r8DAQNWsWdP2PwieyNPzuNTq1as1cOBAeXl5ledwGMHTMzl+/Ljq1q2rnj17ysvLS3/4wx9Up04dj57fPT2TDRs2qE+fPmrVqpV8fHw0adIkbd++3T2ZWA46fPiw1bp1a6tHjx7WuHHjrNatW1tvvfWWo5tfZt68eVabNm2sAQMGWHfeeafVunVrKzU1tcR1P/jgA6t169bWgw8+aP3nP/+xnn/+eeuGG26woqOjy92/O9x6663WN998Y1mWZb3//vvWjTfeaL333ntWfn6+9eqrr1o9e/a0nn32WSsnJ8fatGmT1aFDByszM9OyLMuKj4+39u3bZxUUFFg//PCD1aVLF2vt2rWWZVnWwYMHrQ4dOljbt2+3cnJyrJdeeslq06aNra+lS5daQ4YMsY4dO2bl5ORYM2fOtB577LFSxzl27FirU6dO1o033miFhYVZYWFh1g033GB16tTJuv/++y3Lsqznn3/emjVrVrHt7rjjDuuzzz5z+XFzp6qUycW6d+9uxcfHu/pwuV1VzePiPjxNVcykaL2QkBArNjbWHYfNrapaJp988on10EMPXbZvnqIq5VH0u1y3bt2s7t27W9HR0dapU6fcefjcoiplsnr1auvOO++0/vrXv1oRERHWnXfeye9aBswjlmVZR44csW644QYrOTnZ1YfM7apSJvn5+dbIkSOtdevWWfn5+dbatWut7t27W1lZWe48hC5XlTJ58cUXrWeeeca2zfHjx63WrVvbxuRKDhcrcnJyrOPHj1uW9f+TnTPFirS0NCs7O9uyrAuFi9KKFTk5OdbNN99sjRkzptjrRdt8//335R6Dq136Q9i7d2/bsn379lmtW7e20tLSbK9FRERYe/fuLbGtOXPmWH/9618ty7KsmJiYYj9U586ds9q2bWvrq0+fPtaWLVtsy0+cOGG1adPGysvLK3WsP/30kzVo0CDLsizrjTfesBYtWlRs+fTp063/+Z//KfbasGHDrPfff7/0A2CgqpTJxapKsaKq5PHDDz9YnTt3trZv317qOqaqqplkZWVZy5cvtzZs2FDqOqaqSplkZmZavXv3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ppCQECUkJLiqaQAAAAAAUI14WZZlOdtIQUGB+vTpo8LCQj3wwAPy8/NT165d1ahRozK3+/HHHxUXF6fNmzcrOTlZPj4+atWqlSZMmKCuXbs6OywAAAAAAOCBXHJlxbFjx5ScnKzRo0drxIgRGjBggN1ChSS99957evfdd9WmTRtNmzZNEyZMUGZmpu677z69++67rhgaAAAAAADwMC65smLPnj0aMmSIXnzxRd11110Ob5eYmKjrrrtO/v7+ttdyc3M1ePBgpaamasuWLapRg9tqAAAAAABQnThdrIiOjtbq1auLvda8eXOtX79eqampmj9/vr766iulp6crODhYt9xyi6KjoxUQEFBqm6+99prefPNNbd68WUFBQc4MDwAAAAAAeBhvZxsYNmyYWrRooZiYGA0bNkydOnWSv7+/0tLSNGTIEKWnp2vo0KFq1aqV0tLStHbtWv32229lFitSU1Pl7e2tunXrOjs8AAAAAADgYZwuVoSFhcnLy0sxMTHq0KGDBgwYIOnCFRcnTpzQ8uXLFR4eblt/8uTJKutijuTkZP33v/9VVFSUateu7ezwAAAAAACAh3HLDSEKCwu1du1ade/evVihooiXl1eJ22VnZ+vPf/6zfH19NX36dHcMDQAAAAAAGM7pKytKkp6erszMTLVu3drhbfLy8vToo4/q4MGDWrhwoZo1a+aOoQEAAAAAAMO5pVhR9DGP0q6guFRhYaGefPJJbd68Wa+99pq6dOnijmEBAAAAAAAP4JZiRcOGDRUQEKADBw44tP4zzzyj//73v5ozZ45uv/12dwwJAAAAAAB4CLfcs6JGjRrq3bu3Nm7cqJ07d162/OIbbL788stauXKlpk2bpiFDhrhjOAAAAAAAwIO45coKSfrLX/6ib775RmPHjrU9uvTkyZNau3at5s+frxYtWuif//ynlixZojZt2ig4OFhr1qwp1kbv3r3l5+fnriECAAAAAAADua1YERwcrFWrVun111/XJ598ojNnzig4OFjdunVTgwYNJEl79+61/feJJ564rI0vv/ySYgUAAAAAANWMl3XxZzIAAAAAAAAqmVvuWQEAAAAAAFBeFCsAAAAAAIBRKFYAAAAAAACjUKwAAAAAAABGoVgBAAAAAACMQrECAAAAAAAYhWIFAAAAAAAwirczG2dlZWnx4sVKTExUYmKiTp8+ralT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\n",
      "text/plain": [
       "<Figure size 1080x324 with 30 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "with plt.style.context('seaborn-white'):\n",
    "    fig_maps1 = sbs_cnn1.visualize_outputs(featurizer_layers)\n",
    "    fig_maps2 = sbs_cnn1.visualize_outputs(classifier_layers, y=labels_batch, yhat=predicted)    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[59, 67],\n",
       "        [55, 62],\n",
       "        [71, 71]])"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "StepByStep.loader_apply(sbs_cnn1.val_loader, sbs_cnn1.correct)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
